/*
 * cpu.c
 *
 *  Created on: Feb 4, 2012
 *      Author: Craig Markham and James Marquardt
 *              Class:  TCSS 372
 *              Assignment:
 */

#include <stdlib.h>
#include <time.h>
#include "cpu.h"

/*
 * Mallocs space for all member of the cpu struct.
 */
CPU * initialize(CPU * cpu_ptr){
        cpu_ptr = (CPU *) malloc(12 * sizeof(int));
        register_init(&cpu_ptr->SW);
        register_init(&cpu_ptr->PC);
        register_init(&cpu_ptr->IR);
        register_init(&cpu_ptr->MDR);
        register_init(&cpu_ptr->MAR);

        cpu_ptr->registerFile = registerFile_initialize();
        cpu_ptr->mainMemory = memory_initialize();
        cpu_ptr->alu_ptr = alu_init(cpu_ptr->alu_ptr);
        cpu_ptr->keyboard_ptr = initKeyboard(cpu_ptr->keyboard_ptr);
        cpu_ptr->monitor_ptr = initMonitor(cpu_ptr->monitor_ptr);
        cpu_ptr->macroState = 0;                // one or more integers contain the control state (e.g. fetch, etc)
        cpu_ptr->microState = 0;                // needed for switch inside of switch!
        cpu_ptr->stateMachineStatus = 0;
        cpu_ptr->debugState = 0;
		register_putRegValue(&cpu_ptr->SW, 0);
		cpu_ptr->PC = 0x3000; //Initialize PC to 3000 for Debug Monitor purposes only
    return cpu_ptr;
}

KEYBOARD * initKeyboard(KEYBOARD * keyboard){
	keyboard = (KEYBOARD *) malloc(2 * sizeof(int));
	register_init(&keyboard->status);
	register_init(&keyboard->data);
	
	register_putRegValue(&keyboard->status, 1);
	
	return keyboard;
}

MONITOR * initMonitor(MONITOR * monitor){
	monitor = (MONITOR *) malloc(2 * sizeof(int));
	register_init(&monitor->status);
	register_init(&monitor->data);
	return monitor;
}
/*
 * Progresses through instruction cycle until stateMachineStatus = 0
 * Will wait for key press is debugState is set to STEP.
 */
void cpu_run(CPU * cpu) {
		
		cpu->stateMachineStatus = GO;

        switch(cpu->debugState) {
        case STEP:
                fetch(cpu);
                decode(cpu);
              break;
        case RUN:
              while(cpu->stateMachineStatus == GO) {
                fetch(cpu);
                decode(cpu);
              }
              break;        
    }  
}

/*
 * Loads IR with contents of memory at location indicated by PC.
 * Increments PC.
 */
int fetch(CPU * cpu) {
        int error = 0;

        register_putRegValue(&cpu->IR, memory_fetchWordFrom(cpu->mainMemory,
                                                                        register_getRegValue(&cpu->PC, error), &error));
        register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC, error) + 2);
        return error;
}

/**
 * Determines operation to be performed and performs it.
 */
void decode(CPU * cpu) {
    int the_op = cpu->IR;

    the_op = the_op >> 11;

     switch (the_op) {
            case ALU_1:
                 printf(" ");
				 cpu_runALU(cpu, the_op);
				 if(cpu->alu_ptr->zero_flag == 1) {
					 register_putRegValue(&cpu->SW, cpu->SW | 0x1000);
				 } 
				 else if(cpu->alu_ptr->zero_flag == 0) {
					 register_putRegValue(&cpu->SW, cpu->SW & 0xEFFF);
				 }
				 if (cpu->alu_ptr->positive_flag == 1) {
					 register_putRegValue(&cpu->SW, cpu->SW | 0x0800);
				 }
				 else if (cpu->alu_ptr->positive_flag == 0) {
					 register_putRegValue(&cpu->SW, cpu->SW & 0xF7FF);
				 }
                 break;
            case ALU_2:
                 cpu_runALU(cpu, the_op);
                 break;
            case ALU_3:
                 cpu_runALU(cpu, the_op);
                 break;
            case LDI:
                 cpu_runLDI(cpu);
                 break;
            case LDA:
                 cpu_runLDA(cpu);
                 break;
            case LDB:
                 cpu_runLDB(cpu);
                 break;
            case LDW:
                 cpu_runLDW(cpu);
                 break;
            case STB:
                 cpu_runSTB(cpu);
                 break;
            case STW:
                 cpu_runSTW(cpu);
                 break;
            case PUSHPOP:
                 cpu_runPUSHPOP(cpu);
                 break;
            case BRLOCAL:
                 cpu_runBRLOCAL(cpu);
                 break;
            case BRCREG:
                 cpu_runBRCREG(cpu);
                 break;
            case JSR:
                 cpu_runJSR(cpu);
                 break;
            case JSRC:
                 cpu_runJSRC(cpu);
                 break;
            case TRAP:
                 cpu_runTRAP(cpu);
                 break;
            case RET:
                 cpu_runRET(cpu);
                 break;
            case IO:
                 cpu_runIO(cpu);
                 break;
            case EIDI:
                 cpu_runEIDI(cpu);
                 break;
            case HALT: //HALTS machine by breaking out of while loop.
                 cpu->stateMachineStatus = STOP;
                 break;
            case NOP: //No operation. Skips this cycle.
                 break;
            case LDOS:
                 cpu_runLDOS(cpu);
                 break;
            case LDRO:
                 cpu_runLDRO(cpu);
                 break;
     }
}

/**
 * Performs the appropriate ALU function based on the opcode and the overload op (last 3 bits)
 */
void cpu_runALU(CPU * cpu, int the_op) {
    int error = 0;
    printf("\n ");
     
     int dest_reg = cpu->IR;
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;

     int the_second_op = cpu->IR;

     the_second_op = the_second_op & 0x0007; //gets overload operation
     alu_getOperation(cpu->alu_ptr, the_op, the_second_op);
     
     cpu_fetchOperands(cpu);
     
     alu_perform_operation_primary(cpu->alu_ptr, &error);
     
     registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, cpu->alu_ptr->register_r);
/*     registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg,
                              register_getRegValue(cpu->alu_ptr->register_r, *error));
*/

}

/*
 * Loads immediate value into destination register.
 */
void cpu_runLDI(CPU * cpu) {
		
     int dest_reg = cpu->IR;
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     ushort immediate_val = cpu->IR;
     immediate_val = immediate_val & 0x7F;
     
     registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, immediate_val);
}

/**
 * Loads contents of address pointed to by PC into destination register.
 */
void cpu_runLDA(CPU * cpu) {
        int error = 0;

     int dest_reg = cpu->IR;
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     int address = memory_fetchWordFrom(cpu->mainMemory, (ushort) cpu->PC, &error);
	 registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, (ushort) address);

	 register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC, error) + 2);
}

/**
 * Determines addressing type to perform a load byte function.
 */
void cpu_runLDB(CPU * cpu) {
        int error = 0;
     
     int overload_op = register_getRegValue(&cpu->IR, error);
     overload_op = overload_op & 0x7;
     
     int dest_reg = register_getRegValue(&cpu->IR, error);
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     int mem_addr_reg = register_getRegValue(&cpu->IR, error);
     mem_addr_reg = mem_addr_reg >> 3;
     mem_addr_reg = mem_addr_reg & 0xF;
     
     int mem_addr = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr_reg, &error);
     
     uchar lob = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr, &error);
     lob = lob & 0xFF;
     
     uchar hob = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr, &error);
     hob = hob >> 8;
     hob = hob & 0xFF;
     
     switch(overload_op) {
                         //Loads byte from memory location indicated in register indicated..
                         case 0:
                              registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg,
                                                       memory_fetchByteFrom(cpu->mainMemory,
                                                                   mem_addr, &error));
                              break;
                         //Looads byte from memory location formed from the contents of indicated
                         //register added to contents of the BP register.
                         case 0x1:
                              registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg,
                                                       memory_fetchByteFrom(cpu->mainMemory,
                                                       registerFile_getRegValue(cpu->registerFile,
                                                                   BP, &error) + mem_addr, &error));
                              break;
                         //Loads byte from memory location indicated by contents of IP-0 register.
                         //Increments IP-0 register.
                         case 0x2:
                              registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg,
                                                       memory_fetchByteFrom(cpu->mainMemory,
                                                       registerFile_getRegValue(cpu->registerFile,
                                                                   IP0, &error), &error));
                              registerFile_putRegValue(cpu->registerFile, IP0,
                                          registerFile_getRegValue(cpu->registerFile, IP0, &error) + 1);
                              break;
                         //Loads byte from memory location indicated by the contents of the memory
                         //location indicated in the contents of the source register.
                         case 0x3:
                              registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg,
                                                       memory_fetchByteFrom(cpu->mainMemory,
                                                       memory_fetchWordFrom(cpu->mainMemory,
                                                       mem_addr, &error), &error));
                              break;
     }
}
/**
 * Determines addressing type to perform a load word function.
 */
void cpu_runLDW(CPU * cpu) {
        int error = 0;
     
     int overload_op = register_getRegValue(&cpu->IR, error);
     overload_op = overload_op & 0x7;
     
     int dest_reg = register_getRegValue(&cpu->IR, error);
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     int mem_addr_reg = register_getRegValue(&cpu->IR, error);
     mem_addr_reg = mem_addr_reg >> 3;
     mem_addr_reg = mem_addr_reg & 0xF;
     
     int mem_addr = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr_reg, &error);
     
     uchar lob = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr, &error);
     lob = lob & 0xFF;
     
     uchar hob = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr, &error);
     hob = hob >> 8;
     hob = hob & 0xFF;    
     

     switch(overload_op) {
                 //Loads word from memory location indicated in register indicated..
                 case 0:
                          registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, memory_fetchWordFrom(cpu->mainMemory,
                                                                                           mem_addr, &error));
                          break;
                 //Looads word from memory location formed from the contents of indicated
                 //register added to contents of the BP register.
                 case 0x1:
                          registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, memory_fetchWordFrom(cpu->mainMemory,
                                                                           registerFile_getRegValue(cpu->registerFile, BP, &error) + mem_addr, &error));
                          break;
                 //Loads word from memory location indicated by contents of IP-0 register.
                 //Increments IP-0 register.
                 case 0x2:
                          registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, memory_fetchWordFrom(cpu->mainMemory,
                                                                           registerFile_getRegValue(cpu->registerFile, IP0, &error), &error));
                          break;
                 //Loads byte from memory location indicated by the contents of the memory
                 //location indicated in the contents of the source register.
                 case 0x3:
                          registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, memory_fetchWordFrom(cpu->mainMemory,
                                                                           memory_fetchWordFrom(cpu->mainMemory, mem_addr, &error), &error));
                          break;
     }
}

void cpu_runSTB(CPU * cpu) {

        int error = 0;
 
     int overload_op = register_getRegValue(&cpu->IR, error);
     overload_op = overload_op & 0x7;
     
     int source_reg = register_getRegValue(&cpu->IR, error);
     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;
     
     int mem_addr_reg = register_getRegValue(&cpu->IR, error);
     mem_addr_reg = mem_addr_reg >> 3;
     mem_addr_reg = mem_addr_reg & 0xF;
     
     int mem_addr = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr_reg, &error);
     
     uchar lob = registerFile_getRegValue(cpu->registerFile, source_reg, &error);
     lob = lob & 0xFF;
     
     switch(overload_op) {
                 case 0:
                          memory_storeByteTo(cpu->mainMemory, mem_addr, (uchar) lob);
                          break;
                 case 0x1:
                          memory_storeByteTo(cpu->mainMemory, mem_addr + registerFile_getRegValue(cpu->registerFile,
                                          BP, &error), (uchar) lob);
                          break;
                 case 0x2:
                          memory_storeByteTo(cpu->mainMemory, mem_addr + registerFile_getRegValue(cpu->registerFile,
                                          IP1, &error), (uchar) lob);
                          registerFile_putRegValue(cpu->registerFile, IP1, registerFile_getRegValue(cpu->registerFile,
                                          IP1, &error) + 1);
                          break;
                 case 0x3:
                          memory_storeByteTo(cpu->mainMemory, memory_fetchWordFrom(cpu->mainMemory, mem_addr,
                                          &error), lob);
                          break;
     }
}

void cpu_runSTW(CPU * cpu) {

        int error = 0;

     int overload_op = register_getRegValue(&cpu->IR, error);
     overload_op = overload_op & 0x7;
     
     int source_reg = register_getRegValue(&cpu->IR, error);
     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;
     
     int mem_addr_reg = register_getRegValue(&cpu->IR, error);
     mem_addr_reg = mem_addr_reg >> 3;
     mem_addr_reg = mem_addr_reg & 0xF;
     
     int mem_addr = registerFile_getRegValue(cpu->registerFile, (uchar) mem_addr_reg, &error);
     
     uchar lob = registerFile_getRegValue(cpu->registerFile, source_reg, &error);
     
     switch(overload_op) {
                         
                 case 0:
                          memory_storeWordTo(cpu->mainMemory, mem_addr, (uchar) lob);
                          break;
                 case 0x1:
                          memory_storeWordTo(cpu->mainMemory, mem_addr +
                                          registerFile_getRegValue(cpu->registerFile, BP, &error), (uchar) lob);
                          break;
                 case 0x2:
                          memory_storeWordTo(cpu->mainMemory, mem_addr +
                                          registerFile_getRegValue(cpu->registerFile, IP1, &error), (uchar) lob);
                          registerFile_putRegValue(cpu->registerFile, IP1,
                                          registerFile_getRegValue(cpu->registerFile, IP1, &error) + 1);
                          break;
                 case 0x3:
                          memory_storeWordTo(cpu->mainMemory, memory_fetchWordFrom(cpu->mainMemory,
                                          mem_addr, &error), lob);
                          break;
     }
}

void cpu_runPUSHPOP(CPU * cpu) {
	int error = 0;

    int overload_op = register_getRegValue(&cpu->IR, error);
    overload_op = overload_op & 0x7;
     
    switch(overload_op) {
		 case 0:
				  cpu_runPUSHB(cpu);
				  break;
		 case 0x1:
				  cpu_runPOPB(cpu);
				  break;
		 case 0x2:
				  cpu_runPUSHW(cpu);
				  break;
		 case 0x3:
				  cpu_runPOPW(cpu);
				  break;
     }
}

void cpu_runBRLOCAL(CPU * cpu) {
	int error = 0;
     
     int br_distance = register_getRegValue(&cpu->IR, error);
     br_distance = br_distance & 0x7FF;
     register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC, error) + br_distance);
}

void cpu_runBRREG(CPU * cpu) {
	int error = 0;
     
     int source_reg = register_getRegValue(&cpu->IR, error);
     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;

     register_putRegValue(&cpu->PC, registerFile_getRegValue(cpu->registerFile,
                 (uchar) source_reg, &error));
}

void cpu_runBRCLOCAL(CPU * cpu) {
 
        int error = 0;
     
     int br_distance = register_getRegValue(&cpu->IR, error);
     br_distance = br_distance >> 2;
     br_distance = br_distance & 0x1FF;
     
     int cond_code = register_getRegValue(&cpu->IR, error);
     cond_code = cond_code & 0x3;
     
     switch(cond_code) {
                   case 0:
                                if (cpu->alu_ptr->negative_flag == 1) {
                                         register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC,
                                                         error) + br_distance);
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x1:
                                if (cpu->alu_ptr->zero_flag == 1) {
                                         register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC,
                                                         error) + br_distance);
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x2:
                                if (cpu->alu_ptr->carry_flag == 1) {
                                         register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC,
                                                         error) + br_distance);
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x3:
                                if (cpu->alu_ptr->overflow_flag == 1) {
                                         register_putRegValue(&cpu->PC, register_getRegValue(&cpu->PC,
                                                         error) + br_distance);
                                         break;
                                }
                                else {
                                         break;
                                }
     }
     
}

void cpu_runBRCREG(CPU * cpu) {
        int error = 0;
     
     int source_reg = register_getRegValue(&cpu->IR, error);
     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;
     
     int cond_code = register_getRegValue(&cpu->IR, error);
     cond_code = cond_code & 0x7;
     
	 int new_pc = registerFile_getRegValue(cpu->registerFile, (uchar) source_reg, &error);
	 
     switch(cond_code) {
                   case 0:
                                if (cpu->alu_ptr->negative_flag == 1) {
                                         register_putRegValue(&cpu->PC, registerFile_getRegValue(cpu->registerFile,
                                                         (uchar) source_reg,
                                                  &error));
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x1:
								if (cpu->alu_ptr->zero_flag == 1) {
										
									register_putRegValue(&cpu->PC, new_pc);
								
                                    break;
                                }
                                else {
                                         break;
                                }
                   case 0x2:
                                if (cpu->alu_ptr->carry_flag == 1) {
                                         register_putRegValue(&cpu->PC, registerFile_getRegValue(cpu->registerFile,
                                                         (uchar) source_reg,
                                                  &error));
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x3:
                                if (cpu->alu_ptr->overflow_flag == 1) {
                                         register_putRegValue(&cpu->PC, registerFile_getRegValue(cpu->registerFile,
                                                         (uchar) source_reg,
                                                  &error));
                                         break;
                                }
                                else {
                                         break;
                                }
					case 0x4:
								if (cpu->alu_ptr->positive_flag == 1) {
									register_putRegValue(&cpu->PC, new_pc);
                                    break;
                                }
                                else {
                                      break;
                                }
     }
}

/**
 * Loads destination register with contents of source register.
 */
void cpu_runMOV(CPU * cpu) {
     
        int error = 0;
     
     int dest_reg = register_getRegValue(&cpu->IR, error);
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     int source_reg = register_getRegValue(&cpu->IR, error);
     source_reg = source_reg >> 3;
     source_reg = source_reg & 0xF;
     
     registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg,
                              registerFile_getRegValue(cpu->registerFile,
                              (uchar) source_reg, &error));
}
/**
 * Loads memory location indicated by contents of SP register with the LOB of
 * source register. Decrements SP by 1.
 */
void cpu_runPUSHB(CPU * cpu) {
     
        int * error = 0;
     
     int sp_val = registerFile_getRegValue(cpu->registerFile, SP, error);
     
     int source_reg = register_getRegValue(&cpu->IR, *error);
     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;
     
     int lob = registerFile_getRegValue(cpu->registerFile, source_reg, error);
     lob = lob & 0xFF;
     
     registerFile_putRegValue(cpu->registerFile, SP, sp_val - 1);
     
     sp_val = sp_val - 1;
     
     memory_storeByteTo(cpu->mainMemory, (ushort) sp_val, lob);
     
}
/**
 * Fetches byte from memory location indicated by SP register. Increments SP by 1.
 */
void cpu_runPOPB(CPU * cpu) {
	int * error = 0;

    int sp_val = registerFile_getRegValue(cpu->registerFile, SP, error);
     
     int dest_reg = register_getRegValue(&cpu->IR, *error);
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     ushort lob = memory_fetchByteFrom(cpu->mainMemory, (ushort) sp_val, error);
     
     registerFile_putRegValue(cpu->registerFile, dest_reg, lob);

     registerFile_putRegValue(cpu->registerFile, SP, sp_val + 1);
     
}
/**
 * Loads memory location indicated by contents of SP register with contents of
 * source register. Decrements SP by 2.
 */
void cpu_runPUSHW(CPU * cpu) {
     
     int * error = 0;
     
     int sp_val = registerFile_getRegValue(cpu->registerFile, SP, error);
     
     int source_reg = register_getRegValue(&cpu->IR, *error);
     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;
     
     int word = registerFile_getRegValue(cpu->registerFile, source_reg, error);
     
     registerFile_putRegValue(cpu->registerFile, SP, sp_val - 2);
     
     sp_val = sp_val - 2;
     
     memory_storeWordTo(cpu->mainMemory, (ushort) sp_val, (ushort) word);

}
/**
 * Fetches word from memory location indicated by SP register. Increments SP by 2.
 */
void cpu_runPOPW(CPU * cpu) {
 
        int * error = 0;
     
     int sp_val = registerFile_getRegValue(cpu->registerFile, SP, error);
     
     int dest_reg = register_getRegValue(&cpu->IR, *error);
     dest_reg = dest_reg >> 7;
     dest_reg = dest_reg & 0xF;
     
     ushort word = memory_fetchWordFrom(cpu->mainMemory, (ushort) sp_val, error);
     
     registerFile_putRegValue(cpu->registerFile, (uchar) dest_reg, (ushort) word);

     registerFile_putRegValue(cpu->registerFile, (uchar) SP, (ushort) sp_val + 2);
     
}
/**
 * Loads PC into stack. Loads PC from contents of source register.
 */
void cpu_runJSR(CPU * cpu) {
         int * error = 0;
     
     int sp_val = registerFile_getRegValue(cpu->registerFile, SP, error);
     
     int source_reg = register_getRegValue(&cpu->IR, *error);
     int routine_addr = registerFile_getRegValue(cpu->registerFile, source_reg, error);
     int pc_addr = register_getRegValue(&cpu->PC, *error);

     source_reg = source_reg >> 7;
     source_reg = source_reg & 0xF;
     
     memory_storeWordTo(cpu->mainMemory, (ushort) sp_val - 2, (ushort) pc_addr);
     registerFile_putRegValue(cpu->registerFile, SP, sp_val - 2);
     register_putRegValue(&cpu->PC, (ushort) routine_addr);
     
}
/*
 * Determines which condition code is being examined, then calls cpu_runJSR if
 * condition code is true.
 */
void cpu_runJSRC(CPU * cpu) {
        int * error = 0;
     
     int cond_code = register_getRegValue(&cpu->IR, *error);
     cond_code = cond_code & 0x3;
     
     switch(cond_code) {
                   case 0x0:
                                if(cpu->alu_ptr->negative_flag == 1) {
                                         cpu_runJSR(cpu);
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x1:
                                if(cpu->alu_ptr->zero_flag == 1) {
                                         cpu_runJSR(cpu);
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x2:
                                if(cpu->alu_ptr->carry_flag == 1) {
                                         cpu_runJSR(cpu);
                                         break;
                                }
                                else {
                                         break;
                                }
                   case 0x3:
                                if(cpu->alu_ptr->overflow_flag == 1) {
                                         cpu_runJSR(cpu);
                                         break;
                                }
                                else {
                                         break;
                                }
     }

}

/**
 * Determines register number based on IR. Gets data in specified register and
 * puts it in the appropriate ALU register.
*/
void cpu_fetchOperands(CPU * cpu) {
        int * error = 0;
     
     int operand_a = 0;
     int operand_b = 0;
     
     int operand_a_reg = register_getRegValue(&cpu->IR, *error);
     int operand_b_reg = register_getRegValue(&cpu->IR, *error);

     operand_a_reg = operand_a_reg >> 7;
     operand_a_reg = operand_a_reg & 0xF;
     
     operand_b_reg = operand_b_reg >> 3;
     operand_b_reg = operand_b_reg & 0xF;
     
     operand_a = registerFile_getRegValue(cpu->registerFile, (uchar) operand_a_reg, error);
     operand_b = registerFile_getRegValue(cpu->registerFile, (uchar) operand_b_reg, error);
     
     register_putRegValue(&cpu->alu_ptr->register_a, operand_a);
     register_putRegValue(&cpu->alu_ptr->register_b, operand_b);
}


void cpu_runTRAP(CPU * cpu){

}

void cpu_runRET(CPU * cpu){

}
void cpu_runIO(CPU * cpu){
        int port = cpu->IR >> 1;
        port = port & 0x3F;
        switch(cpu->IR & 0x1) {
                case 0x0:
                        cpu_runIn(cpu, port);
                        break;
                case 0x1:
                        cpu_runOut(cpu, port);
                        break;
                default:
                        break;
        }
}

void cpu_runEIDI(CPU * cpu){

}

void cpu_runLDOS(CPU * cpu){

}

void cpu_runLDRO(CPU * cpu){

}

void cpu_runIn(CPU * cpu, int port){
	int dest = 0;

	switch (port) {
		case 0x38:
			getchar();
			printf("\nInput requested: ");
			char key_input;

			key_input = getchar();
	
			dest = cpu->IR >> 7;
			dest = dest & 0xF;
	
			register_putRegValue(&cpu->keyboard_ptr->data, (uchar) key_input);
	
			if(key_input == '\n') {
				register_putRegValue(&cpu->keyboard_ptr->status, 0);
			}
			else if(key_input != '\n') {
				register_putRegValue(&cpu->keyboard_ptr->status, (uchar) 1);
			}
	
			registerFile_putRegValue(cpu->registerFile, (uchar) dest, cpu->keyboard_ptr->status);
			break;
		
		case 0x3A:
			registerFile_putRegValue(cpu->registerFile, (uchar) dest, cpu->keyboard_ptr->data);
			default:
			break;
        }
}

void cpu_runOut(CPU * cpu, int port){
	int * error = 0;
    int dest = 0;
	dest = cpu->IR >> 7;
	dest = dest & 0xF;

	cpu_fetchOperands(cpu);

	switch (port) {
		case 0x30:
            * error = registerFile_putRegValue(cpu->registerFile, (uchar) dest, cpu->monitor_ptr->status);
            break;
		case 0x32:
			* error = register_putRegValue(&cpu->monitor_ptr->data, cpu->alu_ptr->register_a);
			printf("%c", cpu->monitor_ptr->data);
			break;
		default:
			break;
	}
}

void cpu_runKeyboardIn(CPU * cpu){
        printf("\nKybd in:");

        int dest = 0;
        char input = 0;
        dest = cpu->IR >> 7;
        dest = dest & 0x3F;
        printf("\nEnter char: ");
        input = getchar();
        registerFile_putByteTo(cpu->registerFile,
                        (uchar) dest, 0, (ushort) input);
}

void cpu_runMonitorOut(CPU * cpu){
        int * error = 0;
        int source = cpu->IR >> 7;
        char result = 0;
        source = source & 0x3F;

        result = registerFile_getByteFrom(cpu->registerFile,
                         (uchar) source, 0, error);

        printf("%c", result);
}

